﻿Imports System
Imports System.Reflection
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Design

Namespace System.Workflow.ComponentModel.Compiler
    Friend NotInheritable Class FieldBindValidator
        Inherits Validator
        ' Methods
        Public Overrides Function Validate(ByVal manager As ValidationManager, ByVal obj As Object) As ValidationErrorCollection
            Dim errors As ValidationErrorCollection = MyBase.Validate(manager, obj)
            Dim bind As FieldBind = [TryCast](obj,FieldBind)
            If (bind Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_UnexpectedArgumentType", New Object() { GetType(FieldBind).FullName }), "obj")
            End If
            Dim validationContext As PropertyValidationContext = [TryCast](manager.Context.Item(GetType(PropertyValidationContext)),PropertyValidationContext)
            If (validationContext Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(BindValidationContext).Name }))
            End If
            Dim activity As Activity = [TryCast](manager.Context.Item(GetType(Activity)),Activity)
            If (activity Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(Activity).Name }))
            End If
            Dim item As ValidationError = Nothing
            If String.IsNullOrEmpty(bind.Name) Then
                item = New ValidationError(SR.GetString("Error_PropertyNotSet", New Object() { "Name" }), &H116) { _
                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Name") _
                }
            Else
                Dim context2 As BindValidationContext = [TryCast](manager.Context.Item(GetType(BindValidationContext)),BindValidationContext)
                If (context2 Is Nothing) Then
                    Dim baseType As Type = BindHelpers.GetBaseType(manager, validationContext)
                    If (Not baseType Is Nothing) Then
                        Dim accessType As AccessTypes = BindHelpers.GetAccessType(manager, validationContext)
                        context2 = New BindValidationContext(baseType, accessType)
                    End If
                End If
                If (Not context2 Is Nothing) Then
                    Dim targetType As Type = context2.TargetType
                    If (item Is Nothing) Then
                        errors.AddRange(Me.ValidateField(manager, activity, bind, New BindValidationContext(targetType, context2.Access)))
                    End If
                End If
            End If
            If (Not item Is Nothing) Then
                errors.Add(item)
            End If
            Return errors
        End Function

        Private Function ValidateField(ByVal manager As ValidationManager, ByVal activity As Activity, ByVal bind As FieldBind, ByVal validationContext As BindValidationContext) As ValidationErrorCollection
            Dim errors As New ValidationErrorCollection
            Dim checkTypes As Boolean = False
            Dim service As IWorkflowCompilerOptionsService = [TryCast](manager.GetService(GetType(IWorkflowCompilerOptionsService)),IWorkflowCompilerOptionsService)
            If (Not service Is Nothing) Then
                checkTypes = service.CheckTypes
            End If
            Dim name As String = bind.Name
            Dim enclosingActivity As Activity = Helpers.GetEnclosingActivity(activity)
            If ((name.IndexOf("."c) <> -1) AndAlso (Not enclosingActivity Is Nothing)) Then
                enclosingActivity = Helpers.GetDataSourceActivity(activity, bind.Name, name)
            End If
            If (enclosingActivity Is Nothing) Then
                Dim error As New ValidationError(SR.GetString("Error_NoEnclosingContext", New Object() { activity.Name }), &H130) { _
                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Name") _
                }
                errors.Add([error])
                Return errors
            End If
            Dim item As ValidationError = Nothing
            Dim dataSourceClass As Type = Helpers.GetDataSourceClass(enclosingActivity, manager)
            If (dataSourceClass Is Nothing) Then
                item = New ValidationError(SR.GetString("Error_TypeNotResolvedInFieldName", New Object() { "Name" }), &H11F) { _
                    .PropertyName = MyBase.GetFullPropertyName(manager) _
                }
            Else
                Dim field As FieldInfo = dataSourceClass.GetField(name, (BindingFlags.FlattenHierarchy Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.Instance))))
                If (field Is Nothing) Then
                    item = New ValidationError(SR.GetString("Error_FieldNotExists", New Object() { MyBase.GetFullPropertyName(manager), name }), &H120) { _
                        .PropertyName = MyBase.GetFullPropertyName(manager) _
                    }
                ElseIf (field.FieldType Is Nothing) Then
                    item = New ValidationError(SR.GetString("Error_FieldTypeNotResolved", New Object() { MyBase.GetFullPropertyName(manager), name }), 290) { _
                        .PropertyName = MyBase.GetFullPropertyName(manager) _
                    }
                Else
                    Dim memberInfo As MemberInfo = field
                    If (((bind.Path Is Nothing) OrElse (bind.Path.Length = 0)) AndAlso ((Not validationContext.TargetType Is Nothing) AndAlso Not ActivityBindValidator.DoesTargetTypeMatch(validationContext.TargetType, field.FieldType, validationContext.Access))) Then
                        item = New ValidationError(SR.GetString("Error_FieldTypeMismatch", New Object() { MyBase.GetFullPropertyName(manager), field.FieldType.FullName, validationContext.TargetType.FullName }), &H13F) { _
                            .PropertyName = MyBase.GetFullPropertyName(manager) _
                        }
                    ElseIf Not String.IsNullOrEmpty(bind.Path) Then
                        memberInfo = MemberBind.GetMemberInfo(field.FieldType, bind.Path)
                        If (memberInfo Is Nothing) Then
                            item = New ValidationError(SR.GetString("Error_InvalidMemberPath", New Object() { name, bind.Path }), 300) { _
                                .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                            }
                        Else
                            If checkTypes Then
                                item = MemberBind.ValidateTypesInPath(field.FieldType, bind.Path)
                                If (Not item Is Nothing) Then
                                    item.PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path")
                                End If
                            End If
                            If (item Is Nothing) Then
                                Dim memberType As Type = If(TypeOf memberInfo Is FieldInfo, [TryCast](memberInfo,FieldInfo).FieldType, [TryCast](memberInfo,PropertyInfo).PropertyType)
                                If Not ActivityBindValidator.DoesTargetTypeMatch(validationContext.TargetType, memberType, validationContext.Access) Then
                                    item = New ValidationError(SR.GetString("Error_TargetTypeDataSourcePathMismatch", New Object() { validationContext.TargetType.FullName }), &H141) { _
                                        .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                    }
                                End If
                            End If
                        End If
                    End If
                    If (item Is Nothing) Then
                        If TypeOf memberInfo Is PropertyInfo Then
                            Dim info3 As PropertyInfo = [TryCast](memberInfo,PropertyInfo)
                            If (Not info3.CanRead AndAlso ((validationContext.Access And AccessTypes.Read) <> 0)) Then
                                item = New ValidationError(SR.GetString("Error_PropertyNoGetter", New Object() { info3.Name, bind.Path }), &H142) { _
                                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                }
                            ElseIf (Not info3.CanWrite AndAlso ((validationContext.Access And AccessTypes.Write) <> 0)) Then
                                item = New ValidationError(SR.GetString("Error_PropertyNoSetter", New Object() { info3.Name, bind.Path }), &H143) { _
                                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                }
                            End If
                        ElseIf TypeOf memberInfo Is FieldInfo Then
                            Dim info4 As FieldInfo = [TryCast](memberInfo,FieldInfo)
                            If (((info4.Attributes And (FieldAttributes.Literal Or FieldAttributes.InitOnly)) <> FieldAttributes.PrivateScope) AndAlso ((validationContext.Access And AccessTypes.Write) <> 0)) Then
                                item = New ValidationError(SR.GetString("Error_ReadOnlyField", New Object() { info4.Name }), &H145) { _
                                    .PropertyName = (MyBase.GetFullPropertyName(manager) & ".Path") _
                                }
                            End If
                        End If
                    End If
                End If
            End If
            If (Not item Is Nothing) Then
                errors.Add(item)
            End If
            Return errors
        End Function

    End Class
End Namespace

